﻿/* DHTreeView class, built using DHTiny JS library by DO BA DUC*/

/*MIT LICENSE

Copyright (c) 2008 Do Ba Duc  All Rights Reserved

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge, 
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 
to whom the Software is furnished to do so, subject to the following conditions: 

The above copyright notice and this permission notice shall be included in all copies or 
substantial portions of the Software. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
$dh.newClass("DHTreeView", DHRichCanvas, {
    init: function(container, props) {
        DHRichCanvas.prototype.init.apply(this, arguments);
        this.nodeHeight = this.nodeHeight || 18;
    },

    expandNode: function(node, data) {        
        if (!$dh.isNone(node) && !$dh.isNone(node.childContainer) && (node.dataCached == true)) {
            node.childContainer.style.display = "";
        }
        else {
            if (!$dh.isNone(data))
                this.insertNodes(node, 0, data);
        }
    },

    collapseNode: function(node) {
        if (node.dataCached != true) {    // No caching -> delete all child nodes
            node.removeChild(node.childContainer);
            node.childContainer = null;
        }
        else {  // Caching now, do not delete any child node
            if (!$dh.isNone(node.childContainer)) {
                node.childContainer.style.display = "none";
            }
        }
    },

    removeNode: function(node) {
        node.parentTreeNode.childContainer.removeChild(node);
    },

    emptyTree: function() {
        this.removeChild(this.childContainer);
        this.childContainer = null;
    },

    insertNodes: function(parentTreeNode, position, data) {
        if ($dh.isNone(parentTreeNode)) {
            parentTreeNode = this.canvas;
            parentTreeNode.level = -1;
        }
        for (var i = data.length - 1; i >= 0; i--)
            this.insertNode(parentTreeNode, position, data[i][0], data[i][1], data[i][2]);
    },

    insertNode: function(parentTreeNode, nodeIndex, title, type, data) {
        if ($dh.isNone(parentTreeNode.childContainer)) { // First time insert a child
            parentTreeNode.childContainer = $dh.$new("DIV");
            $dh.addCh(parentTreeNode, parentTreeNode.childContainer);
            parentTreeNode.childTreeNodes = parentTreeNode.childContainer.childNodes;
            parentTreeNode.childContainer.parentTree = this;
        }

        var container = parentTreeNode.childContainer;
        var node = $dh.$new("DIV"); // Node

        if (nodeIndex > container.childNodes.length - 1) // Final node
            container.appendChild(node);
        else
            container.insertBefore(node, container.childNodes[nodeIndex]);

        node.parentTreeNode = parentTreeNode; // For accessing to parent node later
        node.level = parentTreeNode.level + 1;

        // Data setting
        for (var prop in data)
            node[prop] = data[prop];

        // Draw images    
        this.renderNode(node, title, type, nodeIndex);
        parentTreeNode.dataCached = this.cacheFlag; // Cache data for further use
    },

    getLastNodeFlags: function(node) { // From this node, check each parent node is last node of its parent or not
        var ret = new Array();
        var level = node.level;
        while (level > 0) {
            ret[level] = (node.parentTreeNode.childTreeNodes[node.parentTreeNode.childTreeNodes.length - 1] == node);
            node = node.parentTreeNode;
            level--;
        }
        ret[0] = ($dh.isNone(node.parentTreeNode.childTreeNodes) || node.parentTreeNode.childTreeNodes[node.parentTreeNode.childTreeNodes.length - 1] == node);
        return ret;
    },

    renderNode: function(node, title, type, nodeIndex) {
        if ($dh.isNone(type)) type = 0;
        node.style.cssText = "position: relative; width: 100%;"//line-height:"+this.nodeHeight+"px;";
        node.treeNodeType = type;
        node.nodeStatus = (type == 0) ? "closed" : "";

        // "nodeInfo" is a DIV element that contains Images and Label for the node
        var nodeInfo = document.createElement("div");
        node.nodeInfo = nodeInfo;
        node.appendChild(nodeInfo);
        nodeInfo.style.cssText = "position: relative; width:100%;"; // height:"+ this.nodeHeight + "px;";

        var lastFlags = this.getLastNodeFlags(node);

        // Decorate node
        for (var i = 0; i <= node.level; i++) { // Draw images
            var noImage = (i < node.parentTreeNode.level)
                            && (lastFlags[i] == true) 
                            && (lastFlags[i + 1] == true || $dh.isNone(lastFlags[i + 1]));
            var icon = noImage ? document.createElement("span") : document.createElement("img");

            icon.className = "TreeNodeIcon";

            $dh.css(icon, { width: (this.nodeHeight) + "px"
                        , height: (this.nodeHeight) + "px"
                        , left: (i * this.nodeHeight) + "px"
                //,top : "1px"
            }
                );
            nodeInfo.appendChild(icon);

            //alert(title+ ", level "+i+" : "+noImage);
            if (noImage == true) continue;

            if (i == node.level) {
                switch (type) {
                    case 0: icon.src = this.imagePath + "folder.gif"; break; // Closed container/Folder
                    case 1: icon.src = this.imagePath + "file.gif"; break; // File
                    default: break;
                }
            }

            if (i == node.parentTreeNode.level) {
                if (nodeIndex < node.parentTreeNode.childTreeNodes.length - 1) {
                    icon.src = this.imagePath + "3-angle.gif";
                }
                else icon.src = this.imagePath + "2-angle.gif";
            }
            if (i < node.parentTreeNode.level)
                icon.src = this.imagePath + "v-dash.gif";

            if (i == node.level)
                node.icon = icon;
        }

        node.label = $dh.$new("span", { html: title, className: "TreeNodeLabel" });
        $dh.css(node.label, { cursor: "default", left: ((node.level + 1) * this.nodeHeight + 3) + "px" });
        $dh.addCh(nodeInfo, node.label);

        nodeInfo.onmousedown = function(ev) { this.parentNode.nodeMouseDown(ev) };
        nodeInfo.onclick = function(ev) { this.parentNode.nodeClick(ev) };
        nodeInfo.onmouseover = function(ev) { this.parentNode.nodeMouseOver(ev) };
        nodeInfo.onmouseout = function(ev) { this.parentNode.nodeMouseOut(ev) };

        node.nodeMouseDown = this.nodeMouseDown;
        node.nodeMouseOver = this.nodeMouseOver;
        node.nodeMouseOut = this.nodeMouseOut;
        node.nodeClick = this.nodeClick;

        node.parentTree = this; // Save a pointer to parent to call later
    },

    nodeMouseOver: function(ev) {
        var node = this;
        if (node == node.parentTree.selectedNode)
            return;
        node.label.style.backgroundColor = "#eeeeee";
    },
    nodeMouseOut: function(ev) {
        var node = this;
        if (node.parentTree.selectedNode == node)
            return;
        node.label.style.backgroundColor = "";
    },

    nodeMouseDown: function(ev) {
        var node = this;
        if (!$dh.isNone(node.onNodeMouseDown)) // Call user-defined function
            node.onNodeMouseDown(ev);

        ev = ev || window.event;
        if (ev.preventDefault)
            ev.preventDefault();
        ev.returnValue = false; /**/
    },

    nodeClick: function(ev) {
        var node = this;
        if (node.treeNodeType == 0) {
            if (node.nodeStatus.indexOf("opened") < 0) {
                node.nodeStatus = "opened";
                node.icon.src = node.parentTree.imagePath + "folder_opened.gif";

                if (!$dh.isNone(node.parentTree.onExpandingNode)) // Before expanding
                    node.parentTree.onExpandingNode(node);

                node.parentTree.expandNode(node); // Do Expand cached data

                if (!$dh.isNone(this.onExpandedNode)) // After expanding
                    this.onExpandedNode(node, data);
            }
            else {
                node.nodeStatus = "closed";
                node.icon.src = node.parentTree.imagePath + "folder.gif";

                if (!$dh.isNone(node.parentTree.onCollapsingNode)) // Before collapsing
                    node.parentTree.onCollapsingNode(node);

                node.parentTree.collapseNode(node); // Do collapse

                if (!$dh.isNone(node.parentTree.onCollapsedNode)) // After collapsing
                    node.parentTree.onCollapsedNode(node);
            }
        }

        // Set selected status
        node.parentTree.setSelectedNode(node);

        // Call user-defined function on clicked
        if (!$dh.isNone(node.parentTree.onNodeClick)) // Call user-defined function
            node.parentTree.onNodeClick(node, ev);
    },

    setSelectedNode: function(node) { // Not prepare multiple mode yet
        if (this.selectedNode == node) {
            if (node.nodeStatus.indexOf("-selected") < 0)
                node.nodeStatus += "-selected";
            return;
        }

        if (!$dh.isNone(this.selectedNode)) {
            this.selectedNode.label.style.border = "";
            this.selectedNode.label.style.backgroundColor = "";
            this.selectedNode.label.style.color = "";
            var status = this.selectedNode.nodeStatus;
            this.selectedNode.nodeStatus = status.substr(0, status.indexOf("-selected"));
        }

        node.nodeStatus += "-selected";
        this.selectedNode = node;

        node.label.style.border = "solid 1px yellow";
        node.label.style.backgroundColor = "#ff5b3b";
        node.label.style.color = "white";
    },

    setCacheFlag: function(_cacheFlag) {
        if (!$dh.isNone(_cacheFlag))
            this.cacheFlag = _cacheFlag;
    }
});